﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace SerialPortWindow
{
    public class SerialPortModule
    {
        #region singleton
        private static SerialPortModule _instance;
        public static SerialPortModule Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new SerialPortModule();
                }
                return _instance;
            }
        }
        #endregion

        public int project = 7;

        private SerialPort serialPort;
        private MemoryStream _stream;

        private int[] startMarker = { 0x4c, 0x42 };     // L 76, B 66
        private int[] endMarker = { 0x45, 0x3B };       // E 69, ; 59

        private P7SerialPortMessage calibrateMessage_p7 = new P7SerialPortMessage();
        private P7SerialPortMessage message_p7 = new P7SerialPortMessage();

        private P5SerialPortMessage calibrateMessage_p5 = new P5SerialPortMessage();
        private P5SerialPortMessage message_p5 = new P5SerialPortMessage();
        private BaffleStates baffleState = BaffleStates.None;
        /// <summary>
        /// 碰撞挡板标记
        /// </summary>
        private bool touchBaffle = false;
        private float touchDelayTime;

        private bool isCalibrate = false;
        private bool checkVersion = false;
        /// <summary>
        /// 发送传感器校准参数
        /// </summary>
        private bool sendCalibrate = false;

        private byte[] buffer = new byte[1024];
        private int len;
        private int matchIndex = 0;
        private bool isReadingMessage = false;
        private int result = 0;

        private Thread sendThread;
        private Queue<byte[]> sendQuene;

        private void Reset()
        {
            isCalibrate = false;
            checkVersion = false;
            sendCalibrate = false;
            len = 0;
            matchIndex = 0;
            isReadingMessage = false;
            result = 0;

            baffleState = BaffleStates.None;
            touchBaffle = false;
            touchDelayTime = 0;
            sendQuene.Clear();
        }

        public void Init()
        {
            serialPort = new SerialPort();
            serialPort.BaudRate = 115200;
            serialPort.Parity = Parity.None;
            serialPort.DataBits = 8;
            serialPort.StopBits = StopBits.One;

            _stream = new MemoryStream();

            string[] ports = SerialPort.GetPortNames();
            string str = "";
            for (int i = 0; i < ports.Length; i++)
            {
                str += ports[i] + " ";
            }
            Form1.Log("串口号：" + str);

            sendThread = new Thread(SendThread);
            sendThread.Start();
            sendQuene = new Queue<byte[]>();
        }

        /// <summary>
        /// 异步发送，防止卡死（若串口数据没有被接收，则当前程序会被卡死）
        /// </summary>
        private void SendThread()
        {
            while (true)
            {
                if (sendQuene.Count > 0)
                {
                    var bytes = sendQuene.Dequeue();
                    serialPort.Write(bytes, 0, bytes.Length);
                }
            }
        }

        public void Calibrate()
        {
            if (!IsConnected())
            {
                MessageBox.Show("串口设备未打开");
                return;
            }
            Form1.Log("校准");
            isCalibrate = true;
        }

        public void Connect(string portName)
        {
            if (string.IsNullOrEmpty(portName))
            {
                MessageBox.Show("串口号为空");
                return;
            }

            if (serialPort.IsOpen)
            {
                MessageBox.Show(serialPort.PortName + " 串口已打开");
                return;
            }

            serialPort.PortName = portName;
            serialPort.DataReceived -= OnReceived;
            serialPort.DataReceived += OnReceived;

            try
            {
                serialPort.Open();
                Form1.Log(serialPort.PortName + " 串口已打开");
            }
            catch (Exception e)
            {
                MessageBox.Show(serialPort.PortName + " 串口打开异常：" + e.Message);
                return;
            }
        }

        void OnReceived(object sender, SerialDataReceivedEventArgs e)
        {
            len = serialPort.Read(buffer, 0, buffer.Length);
            if (len > 0)
            {
                //Form1.LogAsync("接收到数据，长度为：" + len);

                if (checkVersion)
                {
                    string version = CutoutMsgString(Encoding.ASCII.GetString(buffer), "V", "#");
                    if (!string.IsNullOrEmpty(version))
                    {
                        MessageBox.Show("版本为：" + version);
                        checkVersion = false;
                    }
                }

                if (project == 7)
                    OnReceiveP7();
                else if (project == 5)
                    OnReceiveP5();
            }
        }

        private void OnReceiveP7()
        {
            if (sendCalibrate)
            {
                string msg = CutoutMsg(Encoding.ASCII.GetString(buffer), 's', 'k');
                if (!string.IsNullOrEmpty(msg))
                {
                    MessageBox.Show(msg);
                    sendCalibrate = false;
                }

                msg = CutoutMsgString(Encoding.ASCII.GetString(buffer), "s", "r");
                if (!string.IsNullOrEmpty(msg))
                {
                    MessageBox.Show(msg);
                    sendCalibrate = false;
                }

                msg = CutoutMsgString(Encoding.ASCII.GetString(buffer), "s", "r");
                if (!string.IsNullOrEmpty(msg))
                {
                    MessageBox.Show(msg);
                    sendCalibrate = false;
                }
            }

            for (int i = 0; i < len; i++)
            {
                if (!isReadingMessage)
                {
                    if (buffer[i] == startMarker[matchIndex])
                    {
                        if (++matchIndex == startMarker.Length)
                        {
                            isReadingMessage = true;
                            matchIndex = 0;
                            _stream.SetLength(0);
                        }
                    }
                    else
                    {
                        matchIndex = 0;
                    }
                }
                else
                {
                    if (buffer[i] == endMarker[matchIndex])
                    {
                        if (++matchIndex == endMarker.Length)
                        {
                            // 完成一条数据
                            byte[] data = _stream.ToArray();
                            Form1.Log("一条完整数据：" + data.Length);
                            //for (int j = 0; j < data.Length; j++)
                            //{
                            //    Form1.LogAsync($"data[{j}]：" + data[j]);
                            //}

                            isReadingMessage = false;
                            matchIndex = 0;
                            _stream.SetLength(0);

                            try
                            {
                                int LeftTopPressure = int.TryParse(Encoding.ASCII.GetString(data, 2, 4), out result) ? result : 0;
                                message_p7.LeftTopPressure = LeftTopPressure;

                                int LeftBottomPressure = int.TryParse(Encoding.ASCII.GetString(data, 6, 4), out result) ? result : 0;
                                message_p7.LeftBottomPressure = LeftBottomPressure;

                                int RightTopPressure = int.TryParse(Encoding.ASCII.GetString(data, 10, 4), out result) ? result : 0;
                                message_p7.RightTopPressure = RightTopPressure;

                                int RightBottomPressure = int.TryParse(Encoding.ASCII.GetString(data, 14, 4), out result) ? result : 0;
                                message_p7.RightBottomPressure = RightBottomPressure;

                                int RightReinPull = int.TryParse(Encoding.ASCII.GetString(data, 18, 4), out result) ? result : 0;
                                message_p7.RightReinPull = RightReinPull;

                                int LeftReinPull = int.TryParse(Encoding.ASCII.GetString(data, 22, 4), out result) ? result : 0;
                                message_p7.LeftReinPull = LeftReinPull;

                                int emergencyStop = int.TryParse(Encoding.ASCII.GetString(data, 26, 1), out result) ? result : 0;
                                message_p7.emergencyStop = emergencyStop;

                                if (isCalibrate)
                                {
                                    calibrateMessage_p7.LeftReinPull = message_p7.LeftReinPull;
                                    calibrateMessage_p7.RightReinPull = message_p7.RightReinPull;
                                    calibrateMessage_p7.LeftTopPressure = message_p7.LeftTopPressure;
                                    calibrateMessage_p7.RightTopPressure = message_p7.RightTopPressure;
                                    calibrateMessage_p7.LeftBottomPressure = message_p7.LeftBottomPressure;
                                    calibrateMessage_p7.RightBottomPressure = message_p7.RightBottomPressure;

                                    isCalibrate = false;
                                }

                                message_p7.LeftBottomPressure = Math.Max(message_p7.LeftBottomPressure - calibrateMessage_p7.LeftBottomPressure, 0);
                                message_p7.RightBottomPressure = Math.Max(message_p7.RightBottomPressure - calibrateMessage_p7.RightBottomPressure, 0);
                                message_p7.LeftTopPressure = Math.Max(message_p7.LeftTopPressure - calibrateMessage_p7.LeftTopPressure, 0);
                                message_p7.RightTopPressure = Math.Max(message_p7.RightTopPressure - calibrateMessage_p7.RightTopPressure, 0);
                                message_p7.RightReinPull = Math.Max(message_p7.RightReinPull - calibrateMessage_p7.RightReinPull, 0);
                                message_p7.LeftReinPull = Math.Max(message_p7.LeftReinPull - calibrateMessage_p7.LeftReinPull, 0);

                                Form1.Log("左下压力：" + message_p7.LeftBottomPressure);
                                Form1.Log("右上压力：" + message_p7.RightTopPressure);
                                Form1.Log("右下压力：" + message_p7.RightBottomPressure);
                                Form1.Log("右缰拉力：" + message_p7.RightReinPull);
                                Form1.Log("左缰拉力：" + message_p7.LeftReinPull);
                                Form1.Log("紧急停止：" + message_p7.emergencyStop);
                            }
                            catch (Exception ex)
                            {
                                Form1.Log("数据解析错误：" + ex.Message);
                            }
                        }
                    }
                    else
                    {
                        _stream.WriteByte(buffer[i]);
                    }
                }
            }
        }

        private void OnReceiveP5()
        {
            if (len > 1 && buffer[0] == 0x5A)
            {
                int messageLen = buffer[1] - 5;
                if (len >= messageLen + 5 && messageLen >= 32)
                {
                    try
                    {
                        message_p5.LeftTop = int.TryParse(Encoding.ASCII.GetString(buffer, 4, 4), out result) ? result : 0;
                        message_p5.LeftBottom = int.TryParse(Encoding.ASCII.GetString(buffer, 8, 4), out result) ? result : 0;
                        message_p5.RightTop = int.TryParse(Encoding.ASCII.GetString(buffer, 12, 4), out result) ? result : 0;
                        message_p5.RightBottom = int.TryParse(Encoding.ASCII.GetString(buffer, 16, 4), out result) ? result : 0;
                        message_p5.LossWeight = int.TryParse(Encoding.ASCII.GetString(buffer, 20, 4), out result) ? result : 0;
                        message_p5.BaffleState = int.TryParse(Encoding.ASCII.GetString(buffer, 24, 4), out result) ? (BaffleStates)result : 0;
                        message_p5.HandrailHeight = int.TryParse(Encoding.ASCII.GetString(buffer, 28, 4), out result) ? result : 0;
                        message_p5.EmergencyCheck = int.TryParse(Encoding.ASCII.GetString(buffer, 32, 1), out result) ? result : 0;
                        message_p5.EmergencyStatus = int.TryParse(Encoding.ASCII.GetString(buffer, 33, 1), out result) ? result : 0;

                        if (isCalibrate)
                        {
                            calibrateMessage_p5.LeftTop = message_p7.LeftReinPull;
                            calibrateMessage_p5.LeftBottom = message_p7.RightReinPull;
                            calibrateMessage_p5.RightTop = message_p7.LeftTopPressure;
                            calibrateMessage_p5.RightBottom = message_p7.RightTopPressure;

                            isCalibrate = false;
                        }

                        message_p5.LeftTop = Math.Max(message_p5.LeftTop - calibrateMessage_p5.LeftTop, 0);
                        message_p5.LeftBottom = Math.Max(message_p5.LeftBottom - calibrateMessage_p5.LeftBottom, 0);
                        message_p5.RightTop = Math.Max(message_p5.RightTop - calibrateMessage_p5.RightTop, 0);
                        message_p5.RightBottom = Math.Max(message_p5.RightBottom - calibrateMessage_p5.RightBottom, 0);

                        message_p5.TouchBaffle = false;
                        if (message_p5.BaffleState != BaffleStates.None && message_p5.BaffleState != baffleState && !touchBaffle)
                        {
                            touchBaffle = true;
                            touchDelayTime = 0f;
                            message_p5.TouchBaffle = true;
                        }

                        baffleState = message_p5.BaffleState;

                        Form1.Log("左上压力值：" + message_p5.LeftTop);
                        Form1.Log("左下压力值：" + message_p5.LeftBottom);
                        Form1.Log("右上压力值：" + message_p5.RightTop);
                        Form1.Log("右下压力值：" + message_p5.RightBottom);
                    }
                    catch (Exception ex)
                    {
                        Form1.Log("数据解析错误：" + ex.Message);
                    }
                }
            }
        }

        public bool IsConnected()
        {
            return serialPort != null && serialPort.IsOpen;
        }

        private string CutoutMsgString(string msgStr, string startStr, string endStr)
        {
            int index1, index2;
            index1 = msgStr.IndexOf(startStr, 0);
            if (index1 == -1) return "";
            index2 = msgStr.IndexOf(endStr, index1 + startStr.Length);
            if (index2 == -1) return "";
            return msgStr.Substring(index1 + startStr.Length, index2 - index1 + startStr.Length);
        }

        private string CutoutMsg(string msgStr, char startStr, char end)
        {
            int index1, index2;
            index1 = msgStr.IndexOf(startStr, 0);
            if (index1 == -1) return "";
            index2 = msgStr.IndexOf(end, index1 + 1);
            if (index2 == -1) return "";
            return msgStr.Substring(index1, index2 - index1 + 1);
        }

        public void Send(string text)
        {
            try
            {
                if (!IsConnected())
                {
                    MessageBox.Show("串口设备未打开");
                    return;
                }
                Form1.Log("发送了：" + text);
                if (text == "VER")
                    checkVersion = true;

                byte[] bytes = Encoding.ASCII.GetBytes(text);
                sendQuene.Enqueue(bytes);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #region P7

        public void SendCalibrate(string text, Sensor sensor)
        {
            if (!IsConnected())
            {
                MessageBox.Show("串口设备未打开");
                return;
            }

            if (string.IsNullOrEmpty(text))
            {
                MessageBox.Show("参数为空");
                return;
            }

            string[] splits = text.Split(',');
            if (splits.Length != 2)
            {
                MessageBox.Show("参数错误，格式为：12.345,-4321");
                return;
            }

            byte[] sendBytes = P7Helper.GetCalibrateBytes(splits[0], splits[1], sensor);
            string sendStr = P7Helper.ToHexStrFromByte(sendBytes);
            Form1.Log($"发送了 {sendStr}");
            sendCalibrate = true;
            sendQuene.Enqueue(sendBytes);
        }

        public void SendClear(Sensor sensor)
        {
            if (!IsConnected())
            {
                MessageBox.Show("串口设备未打开");
                return;
            }

            byte[] sendBytes = P7Helper.GetClearBytes(sensor);
            string sendStr = P7Helper.ToHexStrFromByte(sendBytes);
            Form1.Log($"发送了 {sendStr}");
            sendCalibrate = true;
            sendQuene.Enqueue(sendBytes);
        }

        #endregion

        public void Disconnect()
        {
            if (serialPort != null)
            {
                serialPort.Close();
                if (!serialPort.IsOpen)
                {
                    Form1.Log(serialPort.PortName + " 串口已关闭");
                    Reset();
                }
            }
        }

        public void Close()
        {
            Disconnect();
            if (sendThread != null)
            {
                sendThread.Abort();
                sendThread = null;
            }
        }
    }
}
